2D Array Examples

In these examples, you will create a 2D array that has 3 rows by 5 columns (3x5). Since the ultimate goal is to give the array to IDL for processing, pretend it is an “image.” You will set the first row to all red, the second row to all green, and the third row to all blue. The conceptual layout of the array is as follows:

rrrrr

ggggg

bbbbb

We will see shortly that even though the conceptual 2D layout is the above, the actual layout in linear memory is quite different between SAFEARRAYs and IDL.

In the examples below, the “red” value is really the ASCII character ‘r’, “green” is the ASCII character ‘g’, and so on. Use this scheme so that when you look at the actual memory, you will see the letters “rgb”, which makes it easy to read. It is much less confusing than using the cardinal numbers 1, 2, 3, when you are also talking about ordinal numbering involving 1, 2, 3.

These examples illustrate how different languages store data. You do not need to include such code in your applications to make them work; the wrapper does the conversion for you.

Visual Basic

Here is how to create the RGB array (matrix) in Visual Basic. This example, by default, creates a valid SAFEARRAY that is compliant with the information above, and stored within a Variant when passed as a parameter in a method call (not shown).

Const RED As Byte = 114

Const GREEN As Byte = 103

Const BLUE As Byte = 98

‘ This creates an array with dimension indices 0..2 & 0..4

‘ inclusive:

‘ i.e., it creates a 3x5 array; with “lower bounds” set to 0.

Dim m(2, 4) As Byte

For I = 0 To 4

m(0, I) = RED

m(1, I) = GREEN

m(2, I) = BLUE

Next I

Resulting linear memory:

rgbrgbrgbrgbrgb

Resulting SAFEARRAY.rgsabounds:

[0,5], [0,3]

Note the reversed order!

C++ Using ATL SAFEARRAY Wrapper Objects

This example uses the ATL Safearray wrapper objects: CComSafeArrayBound and CComSafeArray, which wraps the calls to the native Win32 Safearray API calls.

CComSafeArrayBound bound[2];

bound[0].SetCount(3); // 3 rows

bound[1].SetCount(5); // 5 columns

CComSafeArray<byte> matx(bound,2);

long ndx[2];

for ( int i = 0; i < 5; i++ )

{

ndx[0] = 0;

ndx[1] = i;

matx.MultiDimSetAt(ndx,'r');

ndx[0] = 1;

ndx[1] = i;

matx.MultiDimSetAt(ndx,'g');

ndx[0] = 2; ndx[1] = i;

matx.MultiDimSetAt(ndx,'b');

}

Resulting linear memory:

rgbrgbrgbrgbrgb

Resulting SAFEARRAY.rgsabounds:

[0,5], [0,3]

Observe that when the CComSafeArrayBound array is created, it is initialized in the conceptually correct order (i.e., specifying the “3 rows” by “5 columns”). But, if you look at the actual SAFEARRAY.rgsabounds[] element in memory, you see that they were reversed when the array was created.

C++ Using SAFEARRAY API Calls and Creating Different Memory Layout

C++ has the flexibility to create SAFEARRAYs in many different ways. By calling the SAFEARRAY API calls directly and judiciously, you can create a SAFEARRAY with data in a different order than what is normally expected. IDL and traditional SAFEARRAY data ordering are different. This example puts the data into the SAFEARRAY in the same order as IDL expects it. In other words, it puts the data in the opposite order that is used for SAFEARRAYs when you use the API calls to set individual data elements.

First, step back and see how the C++ language stores multidimensional arrays. If you have the following declaration:

byte data[3][5] = {

'r','r','r','r','r',

'g','g','g','g','g',

'b','b','b','b','b' };

the resulting linear memory looks like this:

rrrrrgggggbbbbb

This is the same order that IDL expects. However, C++ accesses the memory in the opposite way that IDL would access the same data. For example, to set the kth element of the first row (0-indexed), here is how the two languages compare:

C++:

data[0][k] = value;

IDL:

data[k,0] = value

However, the resulting linear memory layout is the same.

This example creates the 2D RGB array in C++ using the SAFEARRAY API calls and arranging memory in the same layout as IDL.

// First, create the linear memory in the format: rrrrrgggggbbbbb

byte data[3][5];

for ( int i = 0; i < 5; i++ )

{

data[0][i] = 'r';

data[1][i] = 'g';

data[2][i] = 'b';

}

SAFEARRAYBOUND sab[2];

sab[0].lLbound = 0;

sab[0].cElements = 3; // 3 rows

sab[1].lLbound = 0;

sab[1].cElements = 5; // 5 columns

SAFEARRAY* psa = SafeArrayCreateEx(VT_UI1, 2, sab, NULL);

 

// By copying the source data into the safearray data area,

// we can create the data in a different order. Since the

// source data is in the same order as IDL expects, this creates

// a SAFEARRAY with a non-standard ordering.

memcpy(psa->pvData, data, sizeof(data));

Resulting linear memory:

rrrrrgggggbbbbb

Resulting SAFEARRAY.rgsabounds:

[0,5], [0,3]

The consumer of this array needs some indication that the order is different than standard SAFEARRAYs and that it would not need to be converted before passing off to IDL.

Here is how to create the 2D RGB array in IDL pro code:

arr = BYTARR(5, 3)

for i=0,4 do begin

arr[i,0] = 114B

arr[i,1] = 103B

arr[i,2] = 98B

endfor

Resulting linear memory:

rrrrrgggggbbbbb

Calling help, arr gives the following information:

ARR BYTE = Array[5, 3]